﻿/***********************************************************************************\
| Copyright (c) 2011, Bashar Al-Rawi                                                |
| All rights reserved.                                                              |
|                                                                                   |
| Redistribution and use in source and binary forms, with or without                |
| modification, are permitted provided that the following conditions are met:       |
|    * Redistributions of source code must retain the above copyright               |
|      notice, this list of conditions and the following disclaimer.                |
|    * Redistributions in binary form must reproduce the above copyright            |
|      notice, this list of conditions and the following disclaimer in the          |
|      documentation and/or other materials provided with the distribution.         |
|    * Neither the name of the <organization> nor the                               |
|      names of its contributors may be used to endorse or promote products         |
|      derived from this software without specific prior written permission.        |
|                                                                                   |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   |
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            |
| DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY                |
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        |
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND       |
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT        |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     |
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      |
\***********************************************************************************/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Windows.Resources;
using System.Xml.Linq;
using System.Linq;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
using System.Reflection;
using System.IO;

namespace Arabic.Controls
{
    internal enum LayoutId
    {
        Small,
        Capital,
        Symbols1,
        Symbols2
    }

    public enum KeyboardLanguage
    {
        Arabic,
        English
    };

    public class SIP : System.ComponentModel.INotifyPropertyChanged
    {
        private static SIP instance;
        
        private bool _visible;
        public bool Visible {
            get { return _visible; }
            private set { 
                _visible = value; 
            }
        }
        public KeyboardLanguage Language { get; set; }
        private LayoutId activeLayoutId;
        internal KeyboardLayout ActiveLayout 
        {
            get
            {
                return Layouts[activeLayoutId];
            }
        }

        private double _width;
        public double Width
        {
            get { return _width; }
            set { if (_width == value) return; _width = value; RaisePropertyChange("Width"); }
        }

        private double _height;
        public double Height
        {
            get { return _height; }
            set { if (_height == value) return; _height = value; RaisePropertyChange("Height"); }
        }

        private Brush keyboardBgBrush;
        public Brush KeyboardBgBrush
        {
            get
            {
                if (keyboardBgBrush == null)
                {
                    Color kbdBgColor;
                    if (ApplicationGlobals.IsDarkTheme)
                    {
                        kbdBgColor = new Color { A = 255, B = 23, G = 23, R = 23 };
                    }
                    else
                    {
                        kbdBgColor = new Color { A = 255, B = 223, G = 223, R = 223 };
                    }
                    keyboardBgBrush = new SolidColorBrush(kbdBgColor);
                }
                return keyboardBgBrush;
            }
        }



        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChange(string propertyName)
        {
            System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(propertyName));
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        internal bool IsSpacebar(KeyboardKey key)
        {
            return key.PlatformkeyCode == 32;
        }

        internal bool IsBackspace(KeyboardKey key)
        {
            return key.PlatformkeyCode == 8;
        }

        internal bool IsSpecialKey(KeyboardKey key)
        {
            return this.specialKeys.ContainsKey(key.PlatformkeyCode);
        }

        internal bool IsShiftOn { get; set; }

        internal IDictionary<LayoutId, KeyboardLayout> Layouts { get; private set; }
        private readonly int[] specialKeysList = 
        { 32 /*space*/, 8 /* BS */, 233 /* shift */, 0 /* symbol */, 13 /* enter */, 380 /* more symbols */};
        private readonly IDictionary<int, int> specialKeys = new Dictionary<int, int>();

        static SIP()
        {
            instance = new SIP();
            instance.Initialize();
            
        }

        internal void Update(Object orientationChanged)
        {
            bool notifyUI = (bool)orientationChanged;
            this.ActiveLayout.Update(notifyUI);
            if (notifyUI)
            {
                this.Width = this.ActiveLayout.Width;
                this.Height = this.ActiveLayout.Height;
            }
            
        }

        internal KeyboardLayout SwitchLayout(LayoutId id)
        {
            if (!Layouts.ContainsKey(id))
            {
                throw new ArgumentException("Invalid layout id");
            }
            this.activeLayoutId = id;
            Thread t = new Thread(new ParameterizedThreadStart(Update));
            t.Start(false);
            //Update(false);
            return ActiveLayout;
        }

        private SIP() 
        {
           
        }

        public static SIP Instance { 
            get {
            return instance;
            }
        }

        private IList<KeyRow> GetRows(XElement node)
        {
            IList<KeyRow> keyRows = new List<KeyRow>();
            var rows = from row in node.Descendants("Row")
                       select row;
            foreach (var row in rows)
            {
                KeyRow keysRow = new KeyRow();
                var keys = from key in row.Descendants("Key")
                           select key;
                foreach (var key in keys)
                {
                    try
                    {
                        string keyCode = key.Attribute("KeyCode").Value;
                        int platformKeyCode = int.Parse(key.Attribute("PlatformkeyCode").Value);
                        double width = double.Parse(key.Attribute("Width").Value);
                        double height = double.Parse(key.Attribute("Height").Value);
                        string text = string.Empty;
                        double fontSize = 0;
                        string imageName = string.Empty;
                        if (key.Element("TextBlock") != null)
                        {
                            text = key.Element("TextBlock").Attribute("Text").Value;
                            fontSize = double.Parse(key.Element("TextBlock").Attribute("FontSize").Value);
                        }

                        if (key.Element("Image") != null)
                        {
                            imageName = key.Element("Image").Attribute("Source").Value;
                        }
                        KeyboardKey kbdKey = new KeyboardKey(text, keyCode, platformKeyCode, width, height, fontSize, imageName);
                        keysRow.Keys.Add(kbdKey);
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine("error reading key: " + ex.Message);
                        throw;
                    }
                }
                keyRows.Add(keysRow);
            }
            return keyRows;
        }

        private void Initialize()
        {
            this.Visible = false;
            Assembly assembly = this.GetType().Assembly;
            string[] res = assembly.GetManifestResourceNames();
            foreach (string s in res)
                Debug.WriteLine(s);
            using (Stream xmlStream = assembly.GetManifestResourceStream("Arabic.Controls.Keyboard.xml"))
            {
                XDocument document = XDocument.Load(xmlStream);
                foreach (int key in specialKeysList)
                {
                    specialKeys.Add(key, key);
                }

                InitializeLayouts(document);
            }
        }

        private void InitializeLayouts(XDocument document)
        {
            Layouts = new Dictionary<LayoutId, KeyboardLayout>();
            var layouts = from layout in document.Descendants("Layout")
                       select layout;
            foreach (var layout in layouts)
            {

                LayoutId id = (LayoutId) Enum.Parse(typeof(LayoutId), layout.Attribute("Id").Value, true);
                IList<KeyRow> rows = GetRows(layout);
                Layouts.Add(id, new KeyboardLayout(id, rows));
            }
            SwitchLayout(Layouts.First().Key);
        }

        internal void Show()
        {
            this.Visible = true;

        }
        internal void Hide()
        {
            this.Visible = false;
        }
        
    }
}
